package cn.javaxxw.zxing.spring.boot.template;

import cn.javaxxw.zxing.spring.boot.client.MatrixToImageWriter;
import cn.javaxxw.zxing.spring.boot.utils.BitMatrixUtils;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Base64;

/**
 * 功能描述: 二维码工具类:
 * 生成和解析二维码；可处理有logo二维码
 * @author  Trazen
 * @date  2021/1/21 14:49
 */
public class QrCodeTemplate {

    // 1290*1290 860*860 430*430 344*344 258*258
    /**
     * 258*258
     */
    public static final int QRCODE_258 = 258;
    /**
     * 344*344
     */
    public static final int QRCODE_344 = 344;
    /**
     * 430*430
     */
    public static final int QRCODE_430 = 430;
    /**
     * 860*860
     */
    public static final int QRCODE_860 = 860;
    /**
     * 1290*1290
     */
    public static final int QRCODE_1290 = 1290;
    /**
     * LOGO宽度
     */
    private static final int LOGO_WIDTH = 48;
    /**
     * LOGO高度
     */
    private static final int LOGO_HEIGHT = 48;

    private static final String FORMAT_NAME = "png";

    private static final String BASE64_PREFIX = "data:image/png;base64,";


    /**
     * 生成二维码(内嵌LOGO)
     * @param content  二维码内容
     * @param logo logo
     * @param output 输出流
     * @throws WriterException
     * @throws IOException
     */
    public void qrcode(String content, Image logo, OutputStream output) throws WriterException, IOException {
        qrcode(content, QRCODE_258, QRCODE_258, ErrorCorrectionLevel.H, logo, output);
    }


    /**
     * 生成二维码(内嵌LOGO)
     * @param content 二维码内容
     * @param width 宽度
     * @param height 高度
     * @param logo logo
     * @param output 输出流
     * @throws WriterException
     * @throws IOException
     */
    public void qrcode(String content, int width, int height, Image logo, OutputStream output)
            throws WriterException, IOException {
        qrcode(content, width, height, ErrorCorrectionLevel.H, logo, output);
    }



    /**
     * 生成二维码(内嵌LOGO)
     * @param content 二维码内容
     * @param width 宽度
     * @param height 高度
     * @param level 容错等级
     * @param logo logo
     * @param output 输出流
     * @throws WriterException
     * @throws IOException
     */
    public void qrcode(String content, int width, int height, ErrorCorrectionLevel level, Image logo,
                       OutputStream output) throws WriterException, IOException {
        // 二维码图片
        BufferedImage image = qrcode(content, width, height, level, logo);
        // 输出二维码图片流
        ImageIO.write(image, FORMAT_NAME, output);
    }

    /**
     * 生成二维码(内嵌LOGO)
     * @param content 二维码内容
     * @param logo logo
     * @return
     * @throws WriterException
     * @throws IOException
     */
    public BufferedImage qrcode(String content, Image logo) throws WriterException, IOException {
        return qrcode(content, QRCODE_258, QRCODE_258, ErrorCorrectionLevel.H, logo);
    }

    /**
     * 生成二维码(内嵌LOGO)
     * @param content  二维码内容
     * @param width 宽度
     * @param height 高度
     * @param logo logo
     * @return
     * @throws WriterException
     * @throws IOException
     */
    public BufferedImage qrcode(String content, int width, int height, Image logo) throws WriterException, IOException {
        return qrcode(content, width, height, ErrorCorrectionLevel.H, logo);
    }


    /**
     * 生成二维码(内嵌LOGO)
     * @param content 二维码内容
     * @param width 宽度
     * @param height 高度
     * @param level 容错等级
     * @param logo logo
     * @return
     * @throws WriterException
     * @throws IOException
     */
    public BufferedImage qrcode(String content, int width, int height, ErrorCorrectionLevel level, Image logo)
            throws WriterException, IOException {
        return qrcode(content, width, height, level, logo, LOGO_WIDTH, LOGO_HEIGHT);
    }



    /**
     * 生成二维码(内嵌LOGO)
     * @param content  二维码内容
     * @param width 宽度
     * @param height 高度
     * @param level 容错等级
     * @param logo logo
     * @param logoWidth logo宽度
     * @param logoHeight logo高度
     * @return BufferedImage
     * @throws WriterException
     * @throws IOException
     */
    public BufferedImage qrcode(String content, int width, int height, ErrorCorrectionLevel level, Image logo,
                                int logoWidth, int logoHeight) throws WriterException, IOException {
        // 初始化二维码数据位阵
        BitMatrix matrix = BitMatrixUtils.bitMatrix(content, width, height, level);
        // 生成图片
        BufferedImage image = MatrixToImageWriter.toBufferedImage(matrix);
        // 判断logo是否存在
        if (logo != null) {
            // 插入logo
            BitMatrixUtils.drawLogo(image, logo, logoWidth, logoHeight);
        }
        return image;
    }

    /**
     * 生成二维码
     * @param content 二维码内容
     * @return BufferedImage
     * @throws WriterException
     * @throws IOException
     */
    public BufferedImage qrcode(String content) throws WriterException, IOException {
        return qrcode(content, QRCODE_258, QRCODE_258, ErrorCorrectionLevel.H);
    }


    /**
     * 生成二维码
     * @param content 二维码内容
     * @param width 宽度
     * @param height 高度
     * @return BufferedImage
     * @throws WriterException
     * @throws IOException
     */
    public BufferedImage qrcode(String content, int width, int height) throws WriterException, IOException {
        return qrcode(content, width, height, ErrorCorrectionLevel.H);
    }

    /**
     * 生成二维码
     * @param content 二维码内容
     * @param width 宽度
     * @param height 高度
     * @param level  容错等级
     * @return BufferedImage
     * @throws WriterException
     */
    public BufferedImage qrcode(String content, int width, int height, ErrorCorrectionLevel level)
            throws WriterException {
        // 初始化二维码数据位阵
        BitMatrix bitMatrix = BitMatrixUtils.bitMatrix(content, width, height, ErrorCorrectionLevel.H);
        // 图片处理
        return MatrixToImageWriter.toBufferedImage(bitMatrix);
    }


    /**
     * 生成二维码
     * @param content 二维码内容
     * @param output 输出流
     * @throws WriterException
     * @throws IOException
     */
    public void qrcode(String content, OutputStream output) throws WriterException, IOException {
        qrcode(content, QRCODE_258, QRCODE_258, ErrorCorrectionLevel.H, output);
    }


    /**
     * 生成二维码
     * @param content 二维码内容
     * @param width 宽度
     * @param height  高度
     * @param output 输出流
     * @throws WriterException
     * @throws IOException
     */
    public void qrcode(String content, int width, int height, OutputStream output) throws WriterException, IOException {
        qrcode(content, width, height, ErrorCorrectionLevel.H, output);
    }


    /**
     * 生成二维码
     * @param content 二维码内容
     * @param width 宽度
     * @param height 高度
     * @param level 容错等级
     * @param output 输出流
     * @throws WriterException
     * @throws IOException
     */
    public void qrcode(String content, int width, int height, ErrorCorrectionLevel level, OutputStream output)
            throws WriterException, IOException {
        ImageIO.write(qrcode(content, width, height, level), FORMAT_NAME, output);
    }


    /**
     * 生成二维码并编码为Base64【默认258*258】
     * @param content 二维码内容
     * @return Base64
     * @throws WriterException
     * @throws IOException
     */
    public String qrcodeBase64(String content) throws WriterException, IOException {
        return qrcodeBase64(content, QRCODE_258, QRCODE_258, ErrorCorrectionLevel.H);
    }



    /**
     * 生成二维码并编码为Base64
     * @param content 二维码内容
     * @param width 宽度
     * @param height 高度
     * @return  Base64
     * @throws WriterException
     * @throws IOException
     */
    public String qrcodeBase64(String content, int width, int height) throws WriterException, IOException {
        return qrcodeBase64(content, width, height, ErrorCorrectionLevel.H);
    }


    /**
     * 生成二维码并编码为Base64
     * @param content 二维码内容
     * @param width 宽度
     * @param height 高度
     * @param level  容错等级  @link ErrorCorrectionLevel
     * @return  Base64
     * @throws WriterException
     * @throws IOException
     */
    public String qrcodeBase64(String content, int width, int height, ErrorCorrectionLevel level)
            throws WriterException, IOException {
        try (ByteArrayOutputStream output = new ByteArrayOutputStream();) {
            // 二维码图片
            BufferedImage image = qrcode(content, width, height, level);
            // 输出二维码图片流
            ImageIO.write(image, FORMAT_NAME, output);
            return BASE64_PREFIX + Base64.getEncoder().encodeToString(output.toByteArray());
        }
    }
}
